# encoding=utf-8

# ---------------------------------------------------------------------------------------------------------
#
#  加载文档列表，分析出需要更新的文档，再将word文档转换成markdown文档
#
# ---------------------------------------------------------------------------------------------------------

import logging
import os
import sys
import re
import time
import hashlib
import shutil
import functools

import config


# 常量
# 版本文件拆分符
CONST_VERSION_LIST_SPLIT = ','
CONST_DOT_SPLIT = '.'
# 文档名称拆分符，名称_ID
CONST_DOCUMENT_NAME_SPLIT = '_'
CONST_VERSION_COLUMN_SIZE = 5
# 文档ID正则
CONST_DOCUMENT_NAME_PATTERN = '^\d{11}$'


class Document(object):
    '''文档对象
    '''

    def __init__(self):

        # 文档ID
        self.id = ''
        # 文档名称，不带后缀
        self.name = ''
        # 文档版本号
        self.version = 0
        # 文档最后发布时间戳
        self.last_publish = 0
        # 文档最后的MD5值
        self.md5 = ''

        # 全名，带后缀
        self.full_name = ''
        # 全路径
        self.full_path = ''

        # 特殊字段，用于标识是否引用文档
        self.is_reference = False

    def output(self):
        '''格式化输出
        '''
        return '{},{},{},{},{}'.format(self.id, self.name, self.version, self.last_publish, self.md5)


class Handler(object):
    '''文档处理器
    '''

    def __init__(self, base_path, is_reference) -> None:
        '''base_path: 基础路径
        '''
        super().__init__()

        if base_path is None or len(base_path) == 0:
            logging.error('文档处理基础地址不能为空，程序退出')
            sys.exit()

        # 拼装地址
        self._document_path = base_path + 'document/'
        self._markdown_path = base_path + 'markdown/'
        self._version_list_path = base_path + 'versions.txt'

        # 标记当前处理的是否是引用文档
        self._is_reference = is_reference

    def _load_version_list(self):
        '''读取版本文件，加载文件列表
        '''

        logging.info('加载文档列表 %s', self._version_list_path)

        # 检查版本文件合法性
        if not os.path.exists(self._version_list_path):
            logging.warning('版本文件不存在，按全量新增处理')
            return []
        if not os.path.isfile(self._version_list_path):
            logging.error('版本文件应该是一个文件 %s', self._version_list_path)
            sys.exit()

        # 读取版本文件
        documents = []
        with open(self._version_list_path, mode='r', encoding='utf-8') as f:
            while True:
                line = f.readline()
                if len(line) == 0:
                    break

                line = line.strip()
                if len(line) == 0:
                    continue

                cells = line.split(CONST_VERSION_LIST_SPLIT)
                if len(cells) != CONST_VERSION_COLUMN_SIZE:
                    logging.error('读取版本文件失败，文件格式不正确')
                    return

                # 创建文档信息
                document = Document()
                document.id = cells[0]
                document.name = cells[1]
                document.version = int(cells[2])
                document.last_publish = cells[3]
                document.md5 = cells[4]
                document.is_reference = self._is_reference

                documents.append(document)

        return documents

    def _is_document(self, file):
        file = file.lower()
        # 临时文件，跳过
        if file.startswith('~$'):
            logging.debug('跳过临时文件 %s', file)
            return False
        return file.endswith('.doc') or file.endswith('.docx')

    def _load_document_name(self):
        documentNames = []
        for file in os.listdir(self._document_path):
            if not os.path.isfile(self._document_path + file):
                continue
            if self._is_document(file):
                documentNames.append(file)

        return documentNames

    def _sum_document_md5(self, file_name):
        '''计算文档MD5
        '''

        md5 = hashlib.md5()
        with open(self._document_path + file_name, mode="rb") as f:
            data = f.read()
        md5.update(data)

        return md5.hexdigest()

    def _load_document(self, doc_names):
        '''加载最新文档
        '''

        messages = []
        documents = []

        for doc_name in doc_names:
            index = doc_name.rfind(CONST_DOCUMENT_NAME_SPLIT)
            dot_index = doc_name.rfind(CONST_DOT_SPLIT)

            # 检查文档名格式
            if index == -1:
                messages.append(doc_name)
                continue
            # 检查文档ID格式长度
            if not re.match(CONST_DOCUMENT_NAME_PATTERN, doc_name[index+1:dot_index]):
                messages.append(doc_name + "，ID格式错误，长度必须为11位(8位日期，3位序列号)")
                continue

            # 创建
            document = Document()
            document.id = doc_name[index+1: dot_index]
            document.name = doc_name[0:index]
            document.full_name = doc_name
            document.full_path = self._document_path + doc_name
            document.version = 0
            document.last_publish = round(time.time() * 1000)
            document.md5 = self._sum_document_md5(doc_name)
            document.is_reference = self._is_reference

            documents.append(document)

        if len(messages) > 0:
            logging.warning('存在不合法的文档名称，请检查 %s', messages)
        return documents

    def _refresh_document_version(self, histories, documents):
        history_map = {}
        for h in histories:
            history_map[h.id] = h

        changed = []
        changed_documents = []
        for i, d in enumerate(documents):
            history = history_map.get(d.id)
            # 新增文档
            if history is None:
                changed.append(d.id)
                changed_documents.append(d)
                continue
            # 变更文档
            if history.md5 != d.md5:
                documents[i].version = history.version + 1
                changed.append(d.id)
                changed_documents.append(d)
            else:
                # 无变化
                documents[i].version = history.version
                documents[i].last_publish = history.last_publish
            # 计算删除的文档
            history_map.pop(d.id)

        logging.info('移除文档(%s) %s', len(history_map), list(history_map.keys()))
        logging.info('更新文档(%s) %s', len(changed), changed)

        count = len(history_map) + len(changed)
        return changed_documents, list(history_map.keys()), count

    def _update_version_list(self, documents):
        '''更新版本列表
        '''

        line_count = 0
        with open(self._version_list_path, mode='w', encoding='utf-8') as f:
            for d in documents:
                f.write(d.output() + '\n')
                line_count += 1

        logging.info('版本信息更新完毕(%d)', line_count)

    def _convert_to_markdown(self, documents):
        '''转换到markdown
        '''

        logging.info('转换文档开始(%d)', len(documents))

        # sys.path[0] 为脚本执行根目录
        filter_path = sys.path[0] + '\\filter\\custom_filter.py'
        logging.debug('filter_path=%s', filter_path)

        for d in documents:
            # 清空对应目录
            markdown_context_path = self._markdown_path + d.id
            if os.path.exists(markdown_context_path):
                logging.debug('清理 markdown 目录: %s', markdown_context_path)
                shutil.rmtree(markdown_context_path)

            # 重建
            os.mkdir(markdown_context_path)
            # 切换工作目录
            os.chdir(markdown_context_path)
            logging.debug('切换到工作目录 %s', markdown_context_path)

            # 工作目录为输出目录
            # pandoc --wrap=none "益丰大药房会计核算细则.docx" -o "益丰大药房会计核算细则.md" --extract-media=. -t gfm+pipe_tables -F custom_filter.py
            command = '{} --wrap=none "{}" -o "{}" --extract-media=. -t gfm+pipe_tables -F {}'.format(
                config.PANDOC_PATH,
                d.full_path,
                d.name + '.md',
                filter_path
            )

            # 调用 pandoc 进行转换
            logging.info('转换文档 [%s] [%s]', d.id, d.name)
            logging.debug(command)
            logging.debug('command 返回值: %s', os.system(command))

        logging.info('转换文档完成')

    def handle(self):
        '''处理文档，依次获得(变更的文档、删除的文档、所有文档)
        '''

        print('-------------------------------------------------------------------------------------------------------')

        # 加载历史文档列表
        histories = self._load_version_list()

        # 读取最新文档名称列表
        doc_names = self._load_document_name()

        # 加载最新文档
        documents = self._load_document(doc_names)

        # 更新文档版本号
        changed, deleted, count = self._refresh_document_version(histories, documents)

        # 检查是否有文档变更
        force_update = config.FORCE_UPDATE_REFERENCE if self._is_reference else config.FORCE_UPDATE_HANDBOOK
        if count > 0 or force_update:
            # 对文档进行排序
            # 按ID排序
            def compare(a, b):
                return int(a.id) - int(b.id)
            documents = sorted(documents, key=functools.cmp_to_key(compare))

            # PS：转换有可能失败，若失败，就不会更新版本列表
            changed = documents if force_update else changed

            # 先转换成markdown
            self._convert_to_markdown(changed)

            # 再更新版本文件
            self._update_version_list(documents)
        else:
            logging.info('没有需要处理的文档')

        # 依次返回变更的文档、删除的文档、所有的文档
        return changed, deleted, documents
